from memoriaFisica.asignacionContinua.Ajuste import Ajuste
from memoriaFisica.asignacionContinua.Bloque import Bloque

class AsignacionContinua ():
    #INICIALIZACION
    
    ###constructor
    def __init__(self, tipoAjuste):
        self.ajuste = Ajuste (tipoAjuste, self)
        self.Libres = [Bloque(0, 15)]
        self.Ocupados = []
        self.io = None
        self.mmu = None
        
###############################################################################################################

    #METODOS
    
    # OPERACIONES CON BLOQUES 
  
    def hayEspacioDisponibleEnUnBloque (self, lenProgram):
        encontreLugar = False
        newList = []
        while encontreLugar== False and  len(self.Libres)>0: 
            temporal = self.Libres.pop(0)
            if temporal.getCapacidad() >= lenProgram:
                encontreLugar = True
            newList.append(temporal)
        self.Libres = newList
        return encontreLugar
    
    def agregarLibres (self, bloque): #acomoda de mayor a menor
        self.Libres.append(bloque)
        sorted(self.Libres, key =lambda bloque:bloque.dirInicio)
        self.redimensionarBloquesContinuos(bloque) #redimensiona bloques continuos
        
    def redimensionarBloquesContinuos (self, bloque):
        lista = []
        tieneContinuo= False
        for i in self.Libres: 
            if bloque.getDirFin() +1 == i.getDirInicio ():
                lista.append(Bloque(bloque.getDirInicio(), i.getDirFin()))
                tieneContinuo =True
                break
            else:
                if  bloque.getDirInicio() == i.getDirFin()+1:
                    lista.append(Bloque(i.getDirInicio(), bloque.getDirFin()))
                    tieneContinuo =True
                    break
                else:  
                    lista.append(i)
        if not tieneContinuo:
            lista.append(bloque)
        self.Libres = lista 
    
    def agregarOcupados (self, bloque):
        self.Ocupados.append (bloque)
        sorted(self.Ocupados, key =lambda bloque:bloque.dirInicio)
        
    def eliminarBloqueOcupado (self, direccion):
        for bloque in self.Ocupados: 
            if (bloque.getDirInicio()== direccion):
                self.Ocupados.remove(bloque)
                bloque.setOcupante(None)
                self.Libres.append(bloque)
                return bloque
            break 
            
    # REALIZAR ASIGNACION

    def realizarAsignacionAux(self, program, bloqueOcupado):
        bloque = self.ajuste.asignarMemoria(program)
        if (not self.Libres.__contains__(bloque)):
            self.Libres.append(bloque)
        self.Libres.remove(bloque)
        bloqueOcupado, bloqueLibre = bloque.redimencionarBloque(program)
        if bloqueLibre != None:
            self.agregarLibres(bloqueLibre) #le paso una lista, y que la acomode
        bloqueOcupado.setOcupante(program)
        self.agregarOcupados(bloqueOcupado)
        self.mmu.putProgram(program, bloqueOcupado.getDirInicio())
        return bloqueOcupado

    def realizarAsignacion(self, program):#decvuelve un bloque vacio acorde
        print ('realizar asignacion del programa:', program.getName())
        bloqueOcupado = None
        if self.hayEspacioDisponibleEnUnBloque(program.lenCodigo()):
            bloqueOcupado = self.realizarAsignacionAux(program, bloqueOcupado)
        else:
            if self.hayEspacioDisponibleEnLaMemoria(program.lenCodigo()):
                self.compactar()
                bloqueOcupado = self.realizarAsignacion(program)
            else:
                bloqueOcupado = self.swap(program)
        return bloqueOcupado
            
    
    def hayEspacioDisponibleEnLaMemoria (self, n):
        contadorDeCeldasVacias = 0
        for l in self.Libres:
            contadorDeCeldasVacias += l.getCapacidad()
        return n < contadorDeCeldasVacias
    #SWAP
    def swap (self, program):
        print('swap del programa', program.getName())
        bloque = self.Ocupados.pop(0)
        self.io.agregar(bloque.getOcupante())
        self.mmu.limpiarBloque(bloque)
        self.agregarLibres(bloque)
        return self.realizarAsignacion(program)
    
    #COMPACTACION
    def compactar(self):
        newList= []
        bloque = self.Ocupados.pop(0)
        dirInicio = bloque.getDirInicio()
        bloque.setDirInicio(0)
        bloque.setDirFin(bloque.getDirFin() - dirInicio)
        ultimadirFin = bloque.getDirFin()
        newList.append(bloque)
        while len(self.Ocupados) !=0:
            actual = self.Ocupados.pop(0)
            actual.setDirInicio(ultimadirFin +1)
            actual.setDirFin(actual.getDirInicio()+ actual.getCapacidad())
            ultimadirFin = actual.getDirFin()
            newList.append(actual)
        self.Ocupados = newList
        
###############################################################################################################

    #GET&SET                  
    def getLibres(self):
        return self.Libres
    def setLibres (self, lista):
        self.Libres = lista
    def setMMU (self, mmu):
        self.mmu = mmu
    def setIO(self,io):
        self.io = io
    def getOcupados(self):
        return self.Ocupados